package ru.susu.algebra.centralunits.alternating.tex.local;

import java.math.BigInteger;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.ObjectUtils;

import ru.susu.algebra.centralunits.alternating.initializers.ExponentsInitializer;
import ru.susu.algebra.centralunits.alternating.initializers.LocalUnitsInitializer;
import ru.susu.algebra.centralunits.alternating.initializers.QuadraticFieldsInitializer;
import ru.susu.algebra.centralunits.alternating.initializers.SpecialRowsInitializer;
import ru.susu.algebra.centralunits.alternating.tex.CharacterFieldLemmasGenerator;
import ru.susu.algebra.centralunits.alternating.tex.SpecialCharactersUnionMathMethod;
import ru.susu.algebra.chartable.constructor.AlternatingCharTableUtil;
import ru.susu.algebra.field.quadratic.QuadraticFieldGeneralForm;
import ru.susu.algebra.jtex.ITexElement;
import ru.susu.algebra.jtex.ITexSubItemsElement;
import ru.susu.algebra.jtex.SimpleTexElementWithCode;
import ru.susu.algebra.jtex.StringTexElement;
import ru.susu.algebra.jtex.TexBeginEndElement;
import ru.susu.algebra.jtex.UnionTexElement;
import ru.susu.algebra.jtex.formula.FormatSymbols;
import ru.susu.algebra.jtex.formula.FormulaTexElement;
import ru.susu.algebra.jtex.formula.GreekSymbols;
import ru.susu.algebra.jtex.formula.MathSymbols;
import ru.susu.algebra.jtex.utils.TexUtils;
import ru.susu.algebra.pair.Pair;
import ru.susu.algebra.partition.Partition;
import ru.susu.algebra.properties.IPropertySource;
import ru.susu.algebra.util.CollectionUtils;
import ru.susu.algebra.util.NumberUtilities;
import ru.susu.algebra.util.StringUtilities;

/**
 * @author akargapolov
 * @since: 13.09.2010
 */
public class LocalUnitTheorem extends SpecialCharactersUnionMathMethod
{
	private static final Class[] DEPENDENCIES = {SpecialRowsInitializer.class,  QuadraticFieldsInitializer.class, ExponentsInitializer.class, LocalUnitsInitializer.class};

	public static String getLabelName(Integer rowNumber)
	{
		return "LocalUnitTheorem_" + rowNumber;
	}

	/**
	 * Возвращает конкатенацию префикса c {@link MathSymbols#CDOT} suffix, если коэффициент не 0, иначе zeroCaze
	 * @param prefix префикм
	 * @param c коэффициент
	 * @param suffix суффикс
	 * @param zeroCaze вернет в случае если коэффициент 0
	 */
	private String getCoefficientPresentation(String prefix, BigInteger c, Object suffix, Object zeroCaze)
	{
		return c.equals(BigInteger.ZERO) ? zeroCaze.toString() : prefix + c + MathSymbols.CDOT + " " +suffix;
	}

/*	\begin{theo}\label{sto}
	Допустим, что $u_{20}(\lambda)\in\Un(Z(\Z A_{14}))$. Тогда
	\[
	\lambda=(1+\omega_{13})^{43680n}
	\]
	для подходящего целого $n$.
	\end{theo} */
	protected ITexElement getTheorem(Partition partition, IPropertySource ps) throws Exception
	{
		Integer rowNumber = SpecialRowsInitializer.getRowNumber(ps, partition);
		QuadraticFieldGeneralForm generateUnit = QuadraticFieldsInitializer.getGeneralUnit(ps, partition);
		BigInteger pow = LocalUnitsInitializer.getPow(ps, partition);

		ITexSubItemsElement theorem = TexBeginEndElement.threorem().addSubElement(SimpleTexElementWithCode.label(getLabelName(rowNumber)));
		theorem.addSubElement(StringTexElement.text(
				"Допустим, что $u" + TexUtils.index(rowNumber) + TexUtils.inBrackets(GreekSymbols.LAMBDA) + MathSymbols.IN + "\\Un" +
				TexUtils.inBrackets("Z" + TexUtils.inBrackets("\\Z A" + TexUtils.index(partition.getPresentedNumber()))) + "$. Тогда\n" +
				"\\[\n" +
				GreekSymbols.LAMBDA + " = " + TexUtils.inBrackets(TexUtils.toTexString(generateUnit)) + TexUtils.pow(pow + "n" + TexUtils.index(rowNumber)) +
				"\\]\n" +
				"для подходящего целого $n" + TexUtils.index(rowNumber) + "$."));
		return theorem;
	}

	protected ITexElement getProof(Partition partition, IPropertySource ps) throws Exception
	{
		Integer rowNumber = SpecialRowsInitializer.getRowNumber(ps, partition);
		QuadraticFieldGeneralForm generalUnit = QuadraticFieldsInitializer.getGeneralUnit(ps, partition);
		String generalUnitString = TexUtils.toTexString(generalUnit);
		BigInteger pmod = AlternatingCharTableUtil.calcZX(partition);


		TexBeginEndElement proof = TexBeginEndElement.proof();
		String alphaPlusBetaOmega = GreekSymbols.ALPHA + MathSymbols.PLUS + GreekSymbols.BETA + GreekSymbols.OMEGA;

	/*	По лемме \ref{u13}
		\[
		\lambda=\varepsilon(1+\omega_{13})^k=\alpha+\beta\omega,
		\]
		где $\varepsilon\in\{1,-1\}$, $k,\alpha,\beta\in\Z$. */
		proof.addSubElement(StringTexElement.text(
				"По лемме " + SimpleTexElementWithCode.ref(CharacterFieldLemmasGenerator.getLemmaName(rowNumber)).generateContent() + "\n" +
				"\\[\n" +
				GreekSymbols.LAMBDA + "=" + FormulaTexElement.VAREPSILON + "\\left(" + TexUtils.toTexString(generalUnit) + "\\right)^k = " +
				alphaPlusBetaOmega + ",\n" +
				"\\]\n" +
				"где $" + FormulaTexElement.VAREPSILON + FormulaTexElement.IN + "\\{1,-1\\}$ и $k\\in\\Z$.\n\n"));


	/*	Поймём, что достаточно рассматривать случай, когда $k\geqslant0$.
		Заметим, что
		\[
		\lambda^*=\alpha+\beta\omega^*=\varepsilon(1+\omega_{13}^*)^k=\varepsilon(1+\omega_{13})^{-k}.
		\]
		Тогда
		\[
		u(\lambda)u(\lambda^*)=1.
		\]
		Из алгебраической сопряжённости характеров $\chi_{20}$ и $\chi_{21}$
		получаем, что одновременно $u(\lambda)$ и $u(\lambda^*)$ принадлежат
		$\Un(Z(\Z A_{14}))$. Отсюда и получаем,что достаточно рассматривать
		случай, когда $k\geqslant0$.*/
		proof.addSubElement(StringTexElement.text(
				"Поймём, что достаточно рассматривать случай, когда $k" + MathSymbols.GEQSLANT + "0$.\n" +
				"Заметим, что\n" +
				"\\[\n" +
				GreekSymbols.LAMBDA + "^*=" + alphaPlusBetaOmega + "^*=" + FormulaTexElement.VAREPSILON + TexUtils.inBrackets(generalUnitString + "^*") + "^k=" +
				FormulaTexElement.VAREPSILON + TexUtils.inBrackets(generalUnitString) + TexUtils.pow("-k") + ".\n" +
				"\\]\n" +
				"Тогда\n" +
				"\\[\n" +
				"u" + TexUtils.inBrackets(GreekSymbols.LAMBDA) + "u" + TexUtils.inBrackets(GreekSymbols.LAMBDA + "^*") + "=1.\n" +
				"\\]\n" +
				"Из алгебраической сопряжённости характеров $" + GreekSymbols.CHI + TexUtils.index(rowNumber) + "$ и $" + GreekSymbols.CHI + TexUtils.index(rowNumber+1) + "$\n" +
				"получаем, что одновременно $u(" + GreekSymbols.LAMBDA + ")$ и $u(" + GreekSymbols.LAMBDA + "^*)$ принадлежат\n" +
				"$\\Un(Z(\\Z A" + TexUtils.index(partition.getPresentedNumber()) + "))$. Отсюда и получаем, что достаточно рассматривать\n" +
				"случай, когда $k" + MathSymbols.GEQSLANT + "0$.\n\n"));

		BigInteger alpha = LocalUnitsInitializer.getAlphaCoefficient(ps, partition);
		BigInteger beta = LocalUnitsInitializer.getBetaCoefficient(ps, partition);
	/*	Итак, $k\geqslant0$. Пусть для любого неотрицательного целого $m$
		\[
		(1+\omega_{13})^m=\alpha_n+\beta_m\omega.
		\]
		По лемме \ref{dz20} получим
		\begin{equation}\label{veps20}
		\alpha_k\equiv\varepsilon\pmod{917280}\text{ и }
		\beta_k\equiv0\pmod{917280}.
		\end{equation} */
		proof.addSubElement(StringTexElement.text(
				"Итак, $k" + MathSymbols.GEQSLANT + "0$. Пусть для любого неотрицательного целого $m$\n" +
				"\\[\n" +
				TexUtils.inBrackets(generalUnitString) + "^k=" + GreekSymbols.ALPHA + TexUtils.index("k") + MathSymbols.PLUS + GreekSymbols.BETA + TexUtils.index("k") + GreekSymbols.OMEGA + ".\n" +
				"\\]\n" +
				"По лемме " + SimpleTexElementWithCode.ref(DivisibilityLemma.getLabelName(partition, ps)).generateContent() + " получим\n")).addSubElement(
				TexBeginEndElement.equation().addSubElement(SimpleTexElementWithCode.label("veps" + rowNumber)).addSubElement(StringTexElement.text(
						GreekSymbols.ALPHA + TexUtils.index("k") + MathSymbols.EQUIV + "1" + getCoefficientPresentation("+", alpha.mod(pmod), "t", StringUtilities.EMPTY_STRING) +
							FormulaTexElement.pmod(pmod) + FormulaTexElement.text(" и ") + "\n" +
						GreekSymbols.BETA + TexUtils.index("k") + MathSymbols.EQUIV + getCoefficientPresentation("", beta.mod(pmod), "t", "0") + FormulaTexElement.pmod(pmod) + ".")));

	/*	По китайской теореме об остатках получим, что эти условия
		равносильны
		\begin{equation}
		\label{sr}
		\left\{\begin{aligned}
		 \left\{\begin{aligned}
		\alpha_k\equiv\varepsilon\pmod{64}\\
		\beta_k\equiv0\pmod{64}
		 \end{aligned}\right.\\
		\alpha_k\equiv\varepsilon\pmod{13}\\
		\beta_k\equiv0\pmod{13}
		 \end{aligned}\right.
		\end{aligned}
		\right.
		\end{equation} */
		StringBuffer buffer = new StringBuffer();
		List<BigInteger> mods = LocalUnitsInitializer.getMods(ps, partition);
		for (BigInteger mod : mods)
		{
			buffer = (buffer.length() == 0) ? buffer : buffer.append(MathSymbols.DOUBLE_INV_SLASH + "\n");
			buffer.append(UnionTexElement.union().addSubElement(StringTexElement.text(MathSymbols.LEFT + "\\{"))
				.addSubElement(TexBeginEndElement.aligned().addSubElement(StringTexElement.text(
				GreekSymbols.ALPHA + TexUtils.index("k") + MathSymbols.EQUIV +  "1" + getCoefficientPresentation("+", alpha.mod(mod), "t", StringUtilities.EMPTY_STRING)+
					FormulaTexElement.pmod(mod) + MathSymbols.DOUBLE_INV_SLASH + "\n" +
				GreekSymbols.BETA + TexUtils.index("k") + MathSymbols.EQUIV + getCoefficientPresentation("", beta.mod(mod), "t", "0") + FormulaTexElement.pmod(mod))))
				.addSubElement(StringTexElement.text(MathSymbols.RIGHT + ".")).generateContent());
		}
		proof.addSubElement(StringTexElement.text(
				"По китайской теореме об остатках получим, что эти условия равносильны\n")).addSubElement(TexBeginEndElement.equation()
				.addSubElement(SimpleTexElementWithCode.label("sr" + rowNumber))
				.addSubElement(StringTexElement.text(MathSymbols.LEFT + "\\{"))
				.addSubElement(TexBeginEndElement.aligned().addSubElement(StringTexElement.text(buffer.toString())))
				.addSubElement(StringTexElement.text(MathSymbols.RIGHT + "."))).addSubElement(StringTexElement.newLineDouble());

		BigInteger trace = generalUnit.getTrace();
		BigInteger norm = generalUnit.norm();
		String traceFF = NumberUtilities.toString(trace, false, false);
		String normTF = NumberUtilities.toString(norm.multiply(BigInteger.valueOf(-1)), true, false);
	/*	Согласно лемме \ref{st} имеем следующие рекуррентные соотношения
		\begin{align*}
		\alpha_{n+2}&=3\alpha_{n+1}+\alpha_n,\ \alpha_0=1,\ \alpha_1=2,\\
		\beta_{n+2}&=3\beta_{n+1}+ \beta_n,\ \beta_0=0,\ \beta_1=1,
		\end{align*}
		поскольку $\tr(1+\omega_{13})=3$ и $\Norm(1+\omega_{13})=1$  по лемме
		\ref{om}. Посчитаем последовательности $\{\alpha_m\}_{m=0}^\infty$ и
		$\{\beta_m\}_{m=0}^\infty$ по модулям $64$,$9$, $25$, $49$ и $13$. */
		proof.addSubElement(StringTexElement.text("Имеем следующие рекуррентные соотношения"))
				.addSubElement(TexBeginEndElement.align().setWithoutNumber()	//align*
						.addSubElement(StringTexElement.text(
						GreekSymbols.ALPHA + TexUtils.index("k+2") + "&=" + traceFF + GreekSymbols.ALPHA + TexUtils.index("k+1") + normTF +
						GreekSymbols.ALPHA + TexUtils.index("k") + ",\\ " + GreekSymbols.ALPHA + TexUtils.index("0") + "=1,\\ " +
						GreekSymbols.ALPHA + TexUtils.index("1") + "=" + generalUnit.getA() + "," + MathSymbols.DOUBLE_INV_SLASH + "\n" +

						GreekSymbols.BETA + TexUtils.index("k+2") + "&=" + traceFF + GreekSymbols.BETA + TexUtils.index("k+1") + normTF +
						GreekSymbols.BETA + TexUtils.index("k") + ",\\ " + GreekSymbols.BETA + TexUtils.index("0") + "=0,\\ " +
						GreekSymbols.BETA + TexUtils.index("1") + "=" + generalUnit.getB() + ",")))
				.addSubElement(StringTexElement.text(
						"поскольку $" + MathSymbols.TR + TexUtils.inBrackets(generalUnitString) + " = " + trace + "$ и $" +
						MathSymbols.NORM + TexUtils.inBrackets(generalUnitString) + " = " + norm + "$.\n" +
						"Посчитаем последовательности $\\{" + GreekSymbols.ALPHA + TexUtils.index("k") + "\\}_{n=0}^" + MathSymbols.INFTY + "$ и \n" +
						"$\\{" + GreekSymbols.BETA + TexUtils.index("k") + "\\}_{n=0}^" + MathSymbols.INFTY + "$ по модулям $" +
						CollectionUtils.toString(mods, ",") + "$.")).addSubElement(StringTexElement.newLineDouble());

	/*	\item
		По модулю $13$ имеем
		\begin{align*}
		\{\alpha_m\}_{m=0}^\infty= \{1, 1, 4, 0, 4, 12, 1, 2, 7, 10, 11, 4,\\
		10, 8, 8, 6, 0, 6, 5, 8, 3, 4, 2, 10, 6, 2, 12, 12, 9, 0, 9, 1, 12,\\
		11, 6, 3, 2, 9, 3, 5, 5, 7, 0, 7, 8, 5,10, 9, 11, 3, 7, 11, 1, 1,\dots \} \\
		\{\beta_m\}_{m=0}^\infty= \{0, 1, 3, 10, 7, 5, 9, 6, 1, 9, 2, 2, 8,\\
		0, 8, 11, 2, 4, 1, 7, 9, 8, 7, 3, 3, 12, 0, 12, 10, 3, 6, 8, 4, 7,\\
		12, 4, 11, 11, 5, 0, 5, 2, 11, 9, 12, 6, 4, 5, 6, 10, 10, 1, 0,
		1\dots\}
		\end{align*}
		Получаем, что эти последовательности периодичны с периодом $52$,
		причём, так как $\varepsilon=1$, по соотношениям \ref{sr} нам
		подходят
		\begin{equation}
		\left\{\begin{aligned} \alpha_{52l}&\equiv1\pmod{13},\\
		\beta_{52l}&\equiv 0\pmod{13}
		\end{aligned}\right.
		\end{equation}
		для любого целого $r\geqslant0$.*/
		TexBeginEndElement enumerate = TexBeginEndElement.enumerate();
		for (BigInteger mod : mods)
		{
			List<Pair<BigInteger, BigInteger>> period = LocalUnitsInitializer.getPeriod(ps, partition, mod);
			BigInteger intPeriod = LocalUnitsInitializer.getIntegerPeriod(ps, partition, mod);
			enumerate.addSubElement(StringTexElement.text(
					FormatSymbols.ITEM + "\n" +
					"По модулю $" + mod + "$ имеем"));

			if (period.size() <= 500)
			{
				enumerate.addSubElement(TexBeginEndElement.align().setWithoutNumber()	//align*
					.addSubElement(StringTexElement.text("\\{" + GreekSymbols.ALPHA + TexUtils.index("k") + "\\}" + TexUtils.index("k=0") + TexUtils.pow(MathSymbols.INFTY) +
						"= \\{" + getFormattedPeriod(CollectionUtils.getKeys(period)) + "," + FormatSymbols.DOTS + "\\}" + MathSymbols.DOUBLE_INV_SLASH + "\n" +
						"\\{" + GreekSymbols.BETA + TexUtils.index("k") + "\\}" + TexUtils.index("k=0") + TexUtils.pow(MathSymbols.INFTY) +
						"= \\{" + getFormattedPeriod(CollectionUtils.getValues(period)) + "," + FormatSymbols.DOTS + "\\}")));
			}
			enumerate.addSubElement(StringTexElement.text(
					"Получаем, что эти последовательности периодичны с периодом $" + intPeriod + "$,\n" +
					"по соотношениям " + SimpleTexElementWithCode.ref("sr" + rowNumber).generateContent() + " нам\n" +
					"подходят"));
			for (Map.Entry<BigInteger, BigInteger> entry : LocalUnitsInitializer.listGoodPeriods(ps, partition, mod))
			{
				BigInteger alphaC = LocalUnitsInitializer.getAlphaCoefficient(ps, partition).mod(mod);
				BigInteger betaC = LocalUnitsInitializer.getBetaCoefficient(ps, partition).mod(mod);
				BigInteger index = entry.getKey();
				BigInteger t = entry.getValue();
				String sindex = TexUtils.index((ObjectUtils.equals(index, BigInteger.ZERO) ? "" : index + " + ") + intPeriod + "k");
				enumerate.addSubElement(TexBeginEndElement.equation().setWithoutNumber().addSubElement(StringTexElement.text(
					MathSymbols.LEFT + "\\{")).addSubElement(TexBeginEndElement.aligned().addSubElement(StringTexElement.text(
							GreekSymbols.ALPHA + sindex + "&" + getCoefficientPresentation(MathSymbols.EQUIV + "1+", alphaC, t, StringUtilities.EMPTY_STRING) + MathSymbols.EQUIV +
								LocalUnitsInitializer.calcAlpha(ps, partition, t, mod) + FormulaTexElement.pmod(mod) + "," + MathSymbols.DOUBLE_INV_SLASH + "\n" +
							GreekSymbols.BETA + sindex + "&" + getCoefficientPresentation(MathSymbols.EQUIV, betaC, t, StringUtilities.EMPTY_STRING) + MathSymbols.EQUIV +
								LocalUnitsInitializer.calcBeta(ps, partition, t, mod) + FormulaTexElement.pmod(mod))))
					.addSubElement(StringTexElement.text(MathSymbols.RIGHT + ".")));
			}
			enumerate.addSubElement(StringTexElement.text("для любого целого $k" + MathSymbols.GEQSLANT + "0$.\n"));
		}
		proof.addSubElement(enumerate);

	/*	Таким образом, мы должны иметь
		\[
		k=96p=6r=60s=112t=52l.
		\]
		Наименьшее общее кратное чисел $96$, $6$, $60$, $112$ и $52$ равно
		\[
		32\cdot3\cdot5\cdot7\cdot13=43680.
		\]
		Применение леммы \ref{exp} завершает доказательство.*/
		buffer = new StringBuffer("Таким образом, мы должны иметь\n\\[\nk");
		int index = 0;
		List<BigInteger> intPeriods = LocalUnitsInitializer.getReducedPeriods(ps, partition);
		for (BigInteger period : intPeriods)
		{
			buffer.append("=" + period + "p" + TexUtils.index(++index));
		}
		BigInteger lcm = LocalUnitsInitializer.getPow(ps, partition);
		buffer.append(".\n\\]\n").append("Наименьшее общее кратное чисел $" + CollectionUtils.toString(intPeriods, ", ") + "$ равно\n")
				 .append("\\[\n" + TexUtils.getFactorization(lcm) + " = " + lcm + ".\n\\]\n")
				 .append("Применение леммы " + SimpleTexElementWithCode.ref("exp").generateContent() + " завершает доказательство.");
		proof.addSubElement(StringTexElement.text(buffer.toString()));

		return proof;
	}

	@Override
	protected ITexElement getElement(Partition partition, IPropertySource ps) throws Exception
	{
		return UnionTexElement.union().addSubElement(getTheorem(partition, ps)).addSubElement(getProof(partition, ps)).addSubElement(StringTexElement.newLineDouble());
	}

	@Override
	protected Class[] getDependentInitializers()
	{
		return DEPENDENCIES;
	}

	private static String getFormattedPeriod(Collection<BigInteger> collection)
	{
		StringBuffer buffer = new StringBuffer();
		for (Collection<BigInteger>  splitted : CollectionUtils.split(collection, 20))
		{
			buffer = (buffer.length() ==0) ? buffer : buffer.append(", " + MathSymbols.DOUBLE_INV_SLASH + "\n");
			buffer.append(CollectionUtils.toString(splitted, ", "));
		}
		return buffer.toString();
	}
}
